package com.express.gateway.manage.auth;

import com.express.gateway.common.CommonCons;
import com.express.gateway.common.ResultFormat;
import com.express.gateway.common.util.PathUtil;
import com.express.gateway.manage.enums.ContentTypeEnum;
import com.express.gateway.manage.enums.StatusCodeEnum;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.authorization.Authorizations;
import io.vertx.ext.web.RoutingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Objects;
import java.util.Optional;

import static com.express.gateway.common.CommonCons.UNAUTHORIZED_RESULT;
import static com.express.gateway.manage.auth.ClientStaticAuth.*;
import static com.express.gateway.manage.auth.RolesConstant.USER_NAME_KEY;

/**
 * 用于验证用户是否正确
 *
 * @author flower
 */
public class UserAuthUtil {
    private static final Logger LOG = LogManager.getLogger(UserAuthUtil.class);

    /**
     * 检查是否有权限
     *
     * @param user      用户
     * @param authority 权限类型
     * @param handler   处理结果
     */
    public static void isAuthorized(User user, String authority, Handler<AsyncResult<Boolean>> handler) {
        JsonObject o = Optional.ofNullable(user).map(User::principal).filter(json -> !json.isEmpty()).orElse(null);
        if (Objects.isNull(o)) {
            handler.handle(Future.succeededFuture(false));
            return;
        }
        JsonArray roles = user.principal().getJsonArray("roles", new JsonArray());
        handler.handle(Future.succeededFuture(roles.contains(authority)));
    }

    /**
     * 验证用户是否正确,如果正确返回json格式的用户如果不存在返回null
     *
     * @param suser   用户名字
     * @param spass   用户密码
     * @param vertx   vertx
     * @param handler 结果
     */
    public static void auth(String suser, String spass, Vertx vertx, Handler<AsyncResult<JsonObject>> handler) {
        String path = PathUtil.getPathString("user.json");
        vertx.fileSystem().readFile(path).onFailure(ex -> {
            handler.handle(Future.failedFuture(ex.getCause()));
        }).onSuccess(res -> {
            JsonObject users = res.toJsonObject();
            JsonObject user = users.getJsonObject(suser);
            JsonObject urs = Optional.ofNullable(user).filter(u -> spass.equals(u.getString("pwd"))).orElse(null);
            handler.handle(Future.<JsonObject>succeededFuture(urs));
        });
    }


    /**
     * 验证用户是否有权限
     *
     * @param vertx
     * @param handler
     */
    public static void auth(JsonObject authInfo, Vertx vertx, Handler<AsyncResult<Boolean>> handler) {
        String username = authInfo.getString(RolesConstant.USER_NAME_KEY);
        if (Objects.isNull(username)) {
            handler.handle(Future.succeededFuture(false));
            return;
        }
        String path = PathUtil.getPathString("user.json");
        vertx.fileSystem().readFile(path).onFailure(ex -> {
            handler.handle(Future.failedFuture(ex.getCause()));
        }).onSuccess(res -> {
            JsonObject users = res.toJsonObject();
            JsonObject user = users.getJsonObject(username);
            String pwd = authInfo.getString(RolesConstant.USER_PWD_KEY);
            String role = authInfo.getString(RolesConstant.USER_ROLE_KEY);
            JsonObject urs = Optional.ofNullable(user).filter(u -> u.getString("pwd").equals(pwd))
                    .filter(u -> u.getJsonArray("roles").contains(role)).orElse(null);
            handler.handle(Future.succeededFuture(Objects.nonNull(urs)));
        });
    }

    public static  void roleCheck(RoutingContext rct) {
        String path = rct.request().path();
        if (exclude(path)) {
            rct.next();
            return;
        }
        String role = judgmentRoleLevel(path);
        User user = rct.user();
        if (Objects.isNull(user)) {
            LOG.error("user can't access, token is null!");
            rct.response().end(ResultFormat.formatAsZero(StatusCodeEnum.C401));
            return;
        }
        UserAuthUtil.isAuthorized(user, role, res -> {
            if (res.failed()) {
                rct.response().end(ResultFormat.format(StatusCodeEnum.C500, res.cause().getMessage()));
                return;
            }
            if (res.result()) {
                rct.next();
                return;
            }
            rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.HTML_UTF8.val()).end(UNAUTHORIZED_RESULT);
        });
    }

    /**
     * 静态文件不验证 token 和权限
     * @param path 请求路径
     * @return 是否排除
     */
    public static boolean exclude(String path) {
        // String path = rct.request().path();
        if (path.startsWith("/static/framework") || path.startsWith("/static/login.html")) {
            return true;
        }
        return false;
    }

    /**
     * 根据路径判断需要检查的权限
     * @param path 请求路径
     * @return 权限类型
     */
    private static String judgmentRoleLevel(String path) {
        if (path.contains("add")) {
            return RolesConstant.WRITE;
        }
        if (path.contains("del")) {
            return RolesConstant.WRITE;
        }
        if (path.contains("stop")) {
            return RolesConstant.WRITE;
        }
        if (path.contains("start")) {
            return RolesConstant.WRITE;
        }
        if (path.contains("update")) {
            return RolesConstant.WRITE;
        }
        if (path.contains("create")) {
            return RolesConstant.WRITE;
        }
        if (path.contains("online")) {
            return RolesConstant.WRITE;
        }
        if (path.contains("offline")) {
            return RolesConstant.WRITE;
        }
        return RolesConstant.READ;
    }


    /**
     * 欢迎
     *
     * @param rct
     */
    public static void welcome(RoutingContext rct) {
        // TODO 暂未实现欢迎页，直接使用登录做为欢迎页
        loginOut(rct);
    }

    /**
     * 退出登录
     *
     * @param rct
     */
    public static void loginOut(RoutingContext rct) {
        Optional.ofNullable(rct.user()).map(User::authorizations).ifPresent(Authorizations::clear);
        rct.setUser(null);
        rct.session().remove(ClientStaticAuth.AUTHORIZATION);
        rct.session().put(IS_AUTH, false);
        HttpServerRequest request = rct.request();
        String location = String.format("%s://%s:%s/static/login.html", request.scheme(), request.authority().host(), request.authority().port());
        LOG.info("location: {}", location);
        rct.response().putHeader("Location", location).setStatusCode(302).end();
    }

    /**
     * 退出登录
     *
     * @param rct
     */
    public static void loginIn(RoutingContext rct) {
        JsonObject form = rct.body().asJsonObject();
        String username = form.getString("username");
        String password = form.getString("password");
        UserAuthUtil.auth(username, password, rct.vertx(), res -> {
            if (res.failed()) {
                rct.response().end(ResultFormat.format(StatusCodeEnum.C500, res.cause()));
                return;
            }
            JsonObject u = res.result();
            if (Objects.isNull(u)) {
                // LOG.error("user can't access, token is null!");
                rct.response().putHeader(AUTHORIZATION_KEY, AUTH_LOGIN_FAILED).setStatusCode(401).end();
                return;
            }

            u.put(USER_NAME_KEY, username);
            rct.session().put(CommonCons.KEY_USER_NAME, username);
            rct.session().put(IS_AUTH, true);
            rct.session().put(AUTHORIZATION, u);
            rct.setUser(User.create(u));
            HttpServerRequest request = rct.request();
            String location = String.format("%s://%s:%s/static/app/list", request.scheme(), request.authority().host(), request.authority().port());
            LOG.info("ip: {}", location);
            rct.response().end(ResultFormat.format(StatusCodeEnum.C200, location));
        });
    }
}
